package com.example.config;
import com.example.Service.AccountService;
import com.example.entity.RestBean;
import com.example.entity.dto.Account;
import com.example.entity.vo.redponse.AuthorizeVO;
import com.example.filter.jwtAuthorizeFiler;
import com.example.utils.JwtUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 作者:xuSen
 * 日期2025/4/13 11:29
 */
@Configuration
public class SecurityConfiguration {
    @Resource
    private JwtUtils jwtUtils;
    @Resource
    private jwtAuthorizeFiler jwtAuthorizeFiler;
    @Resource
    private AccountService accountService;

    /**
     * 配置Security过滤链
     * <p>
     * 该方法用于定义Web安全配置，包括请求授权、登录、注销、CSRF保护和会话管理
     *
     * @param http HttpSecurity实例，用于配置Web安全设置
     * @return SecurityFilterChain对象，代表配置好的安全过滤链
     * @throws Exception 配置过程中可能抛出的异常
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(conf -> conf
                        .requestMatchers("/api/auth/**","/error").permitAll() // 配置无需认证即可访问的请求路径
                        .requestMatchers("/images/**").permitAll()
                        .anyRequest().authenticated()) // 配置所有其他请求都需要认证
                .formLogin(conf -> conf
                        .loginProcessingUrl("/api/auth/login") // 定义登录请求的处理URL
                        .successHandler(this::onAuthenticationSuccess) // 登录成功后的处理方法
                        .failureHandler(this::onAuthenticationFailure)) // 登录失败后的处理方法
                .logout(conf -> conf
                        .logoutUrl("/api/auth/logout") // 定义注销请求的处理URL
                        .logoutSuccessHandler(this::onLogoutSuccess)) // 注销成功后的处理方法
                .exceptionHandling(conf -> conf
                        // 自定义未授权访问的处理逻辑
                        .authenticationEntryPoint(this::onUnauthorized)
                        // 自定义访问Denied的处理逻辑
                        .accessDeniedHandler(this::onAccessDenied)
                )
                .csrf(AbstractHttpConfigurer::disable) // 禁用CSRF保护，通常在无会话状态的API中使用
                .sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 配置会话管理策略为无状态
                // 在UsernamePasswordAuthenticationFilter之前添加jwtAuthorizeFiler过滤器
                .addFilterBefore(jwtAuthorizeFiler, UsernamePasswordAuthenticationFilter.class)
                // 构建完成
                .build();
    }


    /**
     * 当用户成功认证后调用此方法
     * 当用户成功认证后调用此方法处理成功认证的请求
     * 此方法主要负责在用户成功认证后，向客户端发送一个成功的响应
     * 它通过设置响应的内容类型为JSON，并编写一个表示成功的信息到响应中
     * 这样做的目的是通知客户端用户认证成功，以便进行相应的页面跳转或其他操作
     */
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {

        // 设置响应内容类型为JSON格式，确保客户端正确解析
        response.setContentType("application/json;charset=UTF-8");
        // 从认证对象中获取用户信息
        User user = (User) authentication.getPrincipal();
        // 根据用户名查找用户账户信息
        Account account = accountService.FindByUsernameOrEmail(user.getUsername());
        // 生成JWT令牌
        String token = jwtUtils.createJwt(user, account.getId(), account.getUsername());
        // 创建授权信息对象
        AuthorizeVO authorizeVO = new AuthorizeVO();
        // 将账户信息复制到授权信息对象中
        BeanUtils.copyProperties(account, authorizeVO);
        // 设置令牌过期时间
        authorizeVO.setExpire(jwtUtils.expireTime());
        // 设置用户角色
        //  authorizeVO.setRole(account.getRole());
        // 设置用户名
        // authorizeVO.setUsername(account.getUsername());
        // 设置令牌
        authorizeVO.setToken(token);
        // 将成功响应和授权信息以JSON格式写入响应中
        response.getWriter().write(RestBean.success(authorizeVO).asJsonString());
    }

    /**
     * 当认证失败时调用的方法
     * 此方法主要用于处理认证失败的情况，在认证失败时被调用
     *  通过响应对象向客户端返回错误信息，以JSON格式表示
     *  具体操作包括设置响应内容类型为"application/json;charset=UTF-8"
     *  并将错误信息写入响应体中
     */
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
        // 设置响应内容类型
        response.setContentType("application/json;charset=UTF-8");
        // 将认证失败信息封装为JSON格式并写入响应体
        response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJsonString());
//        response.getWriter().write(RestBean.failure(401,exception.getMessage()).asJsonString());
    }
    /**
     * 处理成功退出登录成功后的方法
     * 当用户成功注销时调用此方法
     * 它主要用于处理注销成功后的操作，例如向客户端返回一个成功的响应
     */
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) throws IOException {
        // 设置响应内容类型为JSON格式，编码为UTF-8
        response.setContentType("application/json;charset=UTF-8");
        // 获取响应输出流，用于向客户端发送数据
        PrintWriter writer = response.getWriter();
        // 从请求头中获取Authorization字段的值，该值可能包含JWT令牌
        String authorization = request.getHeader("Authorization");
        // 调用jwtUtils.invalidateJwt方法检查JWT令牌是否有效
        if(jwtUtils.invalidateJwt(authorization)){
            // 如果JWT令牌无效，通过响应输出流发送成功信息
            writer.write(RestBean.success("退出登录成功").asJsonString());
        }else {
            // 如果JWT令牌有效，通过响应输出流发送失败信息
            writer.write(RestBean.failure(400,"退出登录失败").asJsonString());
        }
    }
    /**
     * 处理访问拒绝异常的方法
     * 当用户尝试访问受保护的资源但没有相应权限时，此方法将被调用
     */
    public void onAccessDenied(HttpServletRequest request,
                               HttpServletResponse response,
                               AccessDeniedException exception) throws IOException{
        // 设置响应的内容类型为JSON格式，并指定字符集为UTF-8
        response.setContentType("application/json;charset=UTF-8");
        // 创建一个表示未授权的RestBean对象，并将其转换为JSON字符串写入响应中
        response.getWriter().write(RestBean.forbidden(exception.getMessage()).asJsonString());
    }


    /**
     * 处理未授权访问请求的回调方法
     * 当用户尝试访问需要权限的资源但未提供有效凭证时调用此方法
     */
    public void onUnauthorized(HttpServletRequest request,
                               HttpServletResponse response,
                               AuthenticationException exception) throws IOException {
        // 设置响应的内容类型为JSON格式，并指定字符集为UTF-8
        response.setContentType("application/json;charset=UTF-8");
        // 创建一个表示未授权的RestBean对象，并将其转换为JSON字符串写入响应中
        response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJsonString());
    }
}
